/*
 * Copyright (C) 2018 Hein-Pieter van Braam
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "android-xml.h"
#include "ResStringPool.h"
#include "ResXMLTreeNode.h"
#include "XMLNameSpace.h"

uint32_t g_lineno = 0;

int main(int argc, char **argv) {
	FILE* fp = fopen(argv[1], "rb");
	if (!fp) {
		perror("Error opening file");
		exit(1);
	}

	fseek(fp, 0, SEEK_END);
	uint32_t filesize = ftell(fp);
	rewind(fp);

	fprintf(stderr, "Got %i bytes of data\n", filesize);
	char* data = (char*) malloc(filesize);
	if (!data) {
		perror("Error allocating");
		exit(1);
	}

	fread(data, 1, filesize, fp);
	fclose(fp);

	uint32_t filepos = 0;
	ResChunk_header cur_header;
	memcpy(&cur_header, data + filepos, sizeof(ResChunk_header));

	if (cur_header.type != RES_XML_TYPE) {
		fprintf(stderr, "Not an Android binary XML file\n");
		exit(1);
	}

	if (cur_header.size != filesize) {
		fprintf(stderr, "Header size does not match filesize\n");
		exit(1);
	}

	filepos += cur_header.headerSize;

	ResXMLTreeNode root(true);
	ResXMLTreeNode* cur = &root;

	ResStringPool* stringpool = nullptr;
	XMLNameSpace* ns = nullptr;

	char* resource_map = nullptr;
	uint32_t resource_map_size = 0;

	while (filepos < filesize) {
		memcpy(&cur_header, data + filepos, sizeof(ResChunk_header));

		switch (cur_header.type) {
		case RES_STRING_POOL_TYPE: {
			fprintf(stderr, "RES_STRING_POOL_TYPE\n");
			stringpool = new ResStringPool(data + filepos);
			break;
		}

		case RES_XML_RESOURCE_MAP_TYPE: {
			fprintf(stderr, "RES_XML_RESOURCE_MAP_TYPE\n");
			// We don't really care what's in here
			resource_map = (char*) malloc(cur_header.size);
			resource_map_size = cur_header.size;
			memcpy(resource_map, data + filepos, cur_header.size);
			break;
		}

		case RES_XML_END_NAMESPACE_TYPE:
			fprintf(stderr, "RES_XML_END_NAMESPACE_TYPE\n");
			break;

		case RES_XML_CDATA_TYPE: {
			printf(
					"Sorry found XML_CDATA_TYPE don't know how to deal with that\n");
			exit(1);
		}

		case RES_XML_LAST_CHUNK_TYPE:
			fprintf(stderr, "RES_XML_LAST_CHUNK_TYPE\n");
			break;

		case RES_XML_START_ELEMENT_TYPE: {
			ResXMLTreeNode* node = new ResXMLTreeNode(*stringpool,
					data + filepos);
			cur->add_child(node);
			cur = node;
			break;
		}

		case RES_XML_END_ELEMENT_TYPE: {
			cur = cur->_parent;
			break;
		}

		case RES_XML_START_NAMESPACE_TYPE: {
			fprintf(stderr, "RES_XML_START_NAMESPACE_TYPE\n");
			ns = new XMLNameSpace(*stringpool, data + filepos);
			break;
		}
		default:
			fprintf(stderr, "Unknown chunk type 0x%03X\n", cur_header.type);
			exit(1);
		}

		filepos += cur_header.size;
	}

	char* xml_serialized;
	uint32_t xml_serialized_size;
	xml_serialized_size = root.serialize(*stringpool, &xml_serialized);
	fprintf(stderr, "XML serialized to %i bytes\n", xml_serialized_size);

	fprintf(stderr, "XML resource map was %i bytes\n", resource_map_size);
	FILE* out = fopen(argv[2], "wb");

	char* ns_serialzed;
	uint32_t ns_serialized_size = 0;
	if (ns) {
		ns_serialized_size = ns->serialize(*stringpool, &ns_serialzed);
		fprintf(stderr, "Namespace serialized to %i bytes\n",
				ns_serialized_size);
	}

	char* ns_end_serialzed;
	uint32_t ns_end_serialized_size = 0;
	if (ns) {
		ns_end_serialized_size = ns->serialize_end(*stringpool,
				&ns_end_serialzed);
		fprintf(stderr, "Namespace end serialized to %i bytes\n",
				ns_end_serialized_size);
	}

	char* stringpool_serialized;
	uint32_t stringpool_serialized_size;
	stringpool_serialized_size = stringpool->serialize(&stringpool_serialized);

	fprintf(stderr, "Stringpool serialized to %i bytes\n",
			stringpool_serialized_size);

	uint32_t serialized_size = sizeof(ResChunk_header) + xml_serialized_size
			+ stringpool_serialized_size + resource_map_size
			+ ns_serialized_size + ns_end_serialized_size;

	fprintf(stderr, "Total size: %i bytes\n", serialized_size);

	ResChunk_header header;
	header.headerSize = sizeof(ResChunk_header);
	header.size = serialized_size;
	header.type = RES_XML_TYPE;

	fwrite(&header, sizeof(ResChunk_header), 1, out);
	fwrite(stringpool_serialized, stringpool_serialized_size, 1, out);
	fwrite(resource_map, resource_map_size, 1, out);
	fwrite(ns_serialzed, ns_serialized_size, 1, out);
	fwrite(xml_serialized, xml_serialized_size, 1, out);
	fwrite(ns_end_serialzed, ns_end_serialized_size, 1, out);

	fclose(out);

	//root.dump(ns);

	stringpool->dump();

	free(xml_serialized);
	free(stringpool_serialized);
	free(ns_serialzed);
	free(data);
	free(resource_map);
	delete (stringpool);
	delete (ns);
}
